#include "GLogin.h"

/*code for class GLoginMap
 *
*/
void GLoginMap::initCreationTime() {
	sincecreated = QDateTime::currentDateTime();
	lastlogin = sincecreated;
}


/*code for class GLogin
 *
*/
GLogin::GLogin(QObject* parent) : QThread(parent) {
	//setup the save timer to save the users list once every minute
	savetimer = new QTimer(this);
	connect(savetimer, SIGNAL(timeout()), this, SLOT(timerSave()));
	savetimer->start(10000);
	
	//setup the verify timer to delete people that have
	//not verified their account in a 30 day timer period
	verifytimer = new QTimer(this);
	connect(verifytimer, SIGNAL(timeout()), this, SLOT(checkVerifies()));
//	verifytimer->start(2592000000);
	
	changed = false;
	
}

int GLogin::linkFriendList(GUser* user, GFriend* ptr, int index, QString name) {
	//setup so i can receive status from friends
	int length = users.size();
	int tmpindex = index;
	//the only way the index of a user could shift is if users
	//before this one were deleted, so search to the left of the know index
	if(index > (length-1)) {
		tmpindex = length - 1;
		for(; tmpindex >= 0; tmpindex--) {
			GLoginMap* tmpptr = users[tmpindex];
			if(tmpptr->name() == name) {
				connect(tmpptr, SIGNAL(sendStatus(QString)), 
								ptr, SLOT(receiveStatusUpdate(QString)));
				tmpptr->initStatus();
				return tmpindex;
			}
			//if the search falls through the user was not found so return -1
			return -1;
		}
	}
	
	//if the initial index is -1 then the users index was never know
	//search the array from the beginning
	if(index == -1) {
		for(tmpindex=length-1; tmpindex>=0; tmpindex--) {
			if(users[tmpindex]->name() == name) {
				connect(users[tmpindex], SIGNAL(sendStatus(QString)), 
								ptr, SLOT(receiveStatusUpdate(QString)));
				users[tmpindex]->initStatus();
				return tmpindex;
			}
		}
		//if the search falls through the user was not found so return -1
		ptr->status(tr("does not exist"));
		return -1;
	}
	
	//found the user setup connections
	if(users[tmpindex]->name() == name) {
		connect(users[tmpindex], SIGNAL(sendStatus(QString)), 
						ptr, SLOT(receiveStatusUpdate(QString)));
		users[tmpindex]->initStatus();
		return tmpindex;
	}
}

GResponse GLogin::login(GUser* user, QString name, QString pass) {
	int length = users.size();
	for(int i=0; i<length; i++) {
		GLoginMap* ptr = users[i];
		if(ptr->name() == name) {
			if(!ptr->verified()) {
				GResponse reply(GResponse::enotverified);
				return reply;
			}
			if(ptr->pass() != pass) {
				GResponse reply(GResponse::eloginpassword);
				return reply;
			}
			if(ptr->user()) {
				GResponse reply(GResponse::ealreadyloggedin);
				return reply;
			}
			ptr->user(user);
			connect(user, SIGNAL(updateStatus(QString)), 
							ptr, SLOT(updateStatus(QString)));
			cout<<"GLogin logged in: " <<name.toStdString() <<endl;
			GResponse reply(GResponse::enone);
			return reply;
		}
	}
	GResponse reply(GResponse::eaccountdoesnotexist);
	return reply;
}

GResponse GLogin::add(QString name, QString pass, QString email) {
	if(name.contains(tr(" "))) {
		GResponse reply(GResponse::echaracters);
		return reply;
	}
	if(name.length() > 15) {
		GResponse reply(GResponse::estringlimit);
		return reply;
	}
	
	int length = users.size();
	for(int i=0; i<length; i++) {
		if(users[i]->name() == name) {
			GResponse reply(GResponse::eaccountalreadyexists);
			return reply;
		}
	}
	
	GLoginMap* temp = new GLoginMap(this);
	temp->initCreationTime();
	temp->name(name);
	temp->pass(pass);
	temp->email(email);
	temp->verified(true );
	
	users.push_back(temp);
	
	QString mkdir(tr("mkdir USERS/")+name);
	system(mkdir.toAscii());
	
	//add image stuff for account verification
	//
	
	//generate the verification string
	QString strver = genRandString(6);
	
	//generate the verification image and save it to
	//USERS/<user>/captcha.jpg
	genRandImage(strver, tr("USERS/")+name+tr("/")+tr("capcha.jpg"));
	
	//save the string sequence to the file USERS/<user>/captcha.txt
	QFile strfile(tr("USERS/")+name+tr("/captcha.txt"));
	strfile.open(QFile::WriteOnly);
	QTextStream strstream(&strfile);
	strstream<<strver;
	strfile.close();
	
	GResponse reply(GResponse::enone);
	changed = true;
	return reply;
}

void GLogin::forgotPassword(QString user) {
	int length = users.size();
	for(int i=0; i<length; i++) {
		if(users[i]->name() == user) {
			
			/*
			QString body;
			body.append("Username: ");
			body.append(users[i]->name());
			body.append("\n");
			body.append("Password: ");
			body.append(users[i]->pass());
			body.append("\n");
			mailer mail(users[i]->email().toAscii(), "mfvarga@eden.rutgers.edu", 
						"Your Password", body.toAscii(), "smtp.rutgers.edu", mailer::SMTP_PORT, false);
			
			string pass = "Goober1";
			
			mail.password(pass);
			
			mail.send();
			*/
			return;
		}
	}
}

void GLogin::logout(GUser* user) {
#ifdef DEBUG
	QString strname;
#endif
	int length = users.size();
	for(int i=0; i<length; i++) {
		GLoginMap* ptr = users[i];
		if(ptr->user() == user) {
#ifdef DEBUG
			strname = ptr->name();	
#endif
			disconnect(ptr, SLOT(updateStatus(QString)));
			ptr->user(0);
		}
	}
#ifdef DEBUG
	cout<<"GLogin logging out: " <<strname.toStdString() <<endl;
#endif
}

void GLogin::shutdown() {
	save();
	int length = users.size();
	for(int i=0; i<length; i++) {
		GLoginMap* temp = users[i];
		temp->user(0);
		delete temp;
	}
	users.clear();
}

void GLogin::initialize() {
	load();
}

void  GLogin::save() {
	QFile file(tr(PASSFILE));
	if(file.open(QIODevice::WriteOnly)) {
		QString fmt("dd/mm/yy/hh:mm:ss:zzz:AP");
		QTextStream stream(&file);
		int length = users.size();
		for(int i=0; i<length; i++) {
			GLoginMap* ptr = users[i];
			stream<<ptr->name() <<" " <<ptr->pass() <<" " <<ptr->email() <<" "
					<<ptr->created().toString(fmt) <<" " 
					<<ptr->lastLogin().toString(fmt) <<" " <<(int)ptr->verified() <<"\n";
		}
	}
	changed = false;
}

void GLogin::load() {
	QFile file(tr(PASSFILE));
	if(file.open(QIODevice::ReadOnly)) {
		QString fmt("dd/mm/yy/hh:mm:ss:zzz:AP");
		QTextStream stream(&file);
		QString line;
		while(!(line = stream.readLine()).isEmpty()) {
			QStringList list(line.split(" ", QString::SkipEmptyParts));
			GLoginMap* l = new GLoginMap(this);
			l->name(list[0]);
			l->pass(list[1]);
			l->email(list[2]);
			l->created(QDateTime::fromString(list[3], fmt));
			l->lastLogin(QDateTime::fromString(list[4], fmt));
			bool ok = false;
			l->verified(list[5].toInt(&ok, 10));
			l->status(tr("offline"));
			users.push_back(l);
		}
	}
}


void GLogin::checkVerifies() {
	QDateTime today = QDateTime::currentDateTime();
	int length = users.size();
	for(int i=0; i<length; i++) {
		GLoginMap* temp = users[i];
		if(!temp->isverified) {
			if(temp->created().addDays(30) < today) {
				temp->user(0);
				delete temp;
				users.removeAt(i);
				length--;
				i--;
			}
		}
	}
}

void GLogin::timerSave() {
	if(changed) save();
}

GResponse GLogin::createMessageChannel(GChatMap* ptr, QString user) {
#ifdef DEBUG
	cout<<"createmessagechannel to: " <<user.toStdString() <<endl;
#endif
	int length = users.size();
	for(int i=0; i<length; i++) {
		if(users[i]->name() == user) {
			//we found the user so setup the connections
			//setup send message connections
			if(users[i]->userptr) {
				//setup connection to send a message
				connect(ptr, SIGNAL(sendMessage(QString,QString)), users[i]->userptr, 
						SLOT(receiveMessage(QString,QString)));
				//setup connection to close the channel
				connect(ptr, SIGNAL(closeMessage(QString)), users[i]->userptr, SLOT(closeMessage(QString)));
#ifdef DEBUG
				cout<<"enone" <<endl;
#endif
				return GResponse(GResponse::enone);
			} else {
#ifdef DEBUG
				cout<<"eusernotonline" <<endl;
#endif
				return GResponse(GResponse::eusernotonline);
			}
		}
	}
#ifdef DEBUG
				cout<<"eaccountdoesnotexist" <<endl;
#endif
	return GResponse(GResponse::eaccountdoesnotexist);
}

GResponse GLogin::createFileTransferChannel(GFileMap* ptr, GUser* cur, QString user) {
#ifdef DEBUG
	cout<<"createfilechannel to: " <<user.toStdString() <<endl;
#endif
	int length = users.size();
	for(int i=0; i<length; i++) {
		if(users[i]->name() == user) {
			//we found the user so setup the connections
			//setup send message connections
			if(users[i]->userptr) {
				//setup connection to send a chunk
				connect(ptr, SIGNAL(sendChunk(QString,QString,QString)), users[i]->userptr, 
						SLOT(receiveFileChunk(QString,QString,QString)));
				//setup a connection to the receiver so he may ask for a  file trasnfer
				connect(ptr, SIGNAL(requestTransfer(QString,QString,int)), users[i]->userptr,
						SLOT(requestFileTransfer(QString,QString,int)));
				//setup the connection so the user may approve the file
				connect(users[i]->userptr, SIGNAL(approveTheTransfer(QString,QString,bool)),
						cur, SLOT(requestTransferResponse(QString,QString,bool)));
				//setup connection to stop the transfer
				connect(users[i]->userptr, SIGNAL(stopFileTransfer(QString,QString)), 
						cur, SLOT(closeTransfer(QString,QString)));
				//connection for when the host wants to close the connection
				connect(ptr, SIGNAL(closeTransfer(QString,QString)), users[i]->userptr,
						SLOT(hostClosedTransfer(QString,QString)));
#ifdef DEBUG
				cout<<"enone" <<endl;
#endif
				return GResponse(GResponse::enone);
			} else {
#ifdef DEBUG
				cout<<"eusernotonline" <<endl;
#endif
				return GResponse(GResponse::eusernotonline);
			}
		}
	}
#ifdef DEBUG
				cout<<"eaccountdoesnotexist" <<endl;
#endif
	return GResponse(GResponse::eaccountdoesnotexist);
}

void GLogin::setAccountVerified(QString user) {
	int length = users.size();
	for(int i=0; i<length; i++) {
		if(users[i]->name() == user) {
			users[i]->verified(true);
			return;
		}
	}
}

QString GLogin::genRandString(int len) {
	time_t newtime = time(NULL);
	static time_t oldtime;
	//store the previous time used
	//so if this time is the same as last a new time
	//needs to be used
	if(oldtime == newtime) {
		sleep(1);
		newtime = time(NULL);
	}
	srand(newtime);
	QString str;
	//generate len number of random characters
	for(int i=0; i<len; i++) {
		int ran = rand() % 63;
		//map the random number to a character
		if(ran <= 9) {
			str.push_back(ran + '0');
		} else if(ran >= 10 && ran <= 36) {
			str.push_back(ran + 'A' - 10);
		} else if(ran >= 37 && ran <= 62) {
			str.push_back(ran + 'a' - 37);
		}
	}
	oldtime = newtime;
	return str;
}

void GLogin::genRandImage(QString txt, QString fn) {
	
	QImage img(150, 40, QImage::Format_RGB32);
	
	img.fill(0xffffff);
	
	QPainter paint(&img);
	paint.setFont(QFont(tr("Helvetica"), 30));
	
	paint.drawText(0, 0, img.width(), img.height(), Qt::AlignCenter, txt);
	
	int width = img.width();
	int height = img.height();
	
	for(int i=-height; i<width; i+=10) {
		paint.drawLine(i, 0, i+height, height);
	}
	
	for(int i=0; i<width*2; i+=10) {
		paint.drawLine(0, i, width, i-width);
	}
	
	img.save(fn);
	
}
